package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"strings"

	"github.com/cryptix/wav"
)

type AudFile struct {
	Num   int
	Name  string
	Delay int
}

func main() {
	flag.Parse()
	d, err := ioutil.ReadDir(flag.Arg(0))
	if err != nil {
		panic(err)
	}
	files := []AudFile{}
	p1 := regexp.MustCompile(`^[0-9]+[:：][0-9]+\.wav$`)
	var i = 0
	for _, v := range d {
		if p1.MatchString(v.Name()) == false {
			continue
		}
		a := AudFile{Num: i, Name: filepath.Join(flag.Arg(0), v.Name()), Delay: getDelayFromName(v.Name())}
		if a.Delay == -1 {
			panic("Convert Delay error:" + v.Name())
		}
		files = append(files, a)
		i++
	}
	sort.Slice(files, func(i, j int) bool {
		return files[i].Delay < files[j].Delay
	})
	err = mergeFilesTo(files, "out.wav")
	if err != nil {
		panic(err)
	}
}

func getDelayFromName(name string) int {
	seps1 := strings.Split(name, ".")
	spliter := regexp.MustCompile("[:：]")
	seps2 := spliter.Split(seps1[0], -1)
	if len(seps2) != 2 {
		return -1
	}
	i1, err := strconv.Atoi(seps2[0])
	if err != nil {
		return -1
	}
	i2, err := strconv.Atoi(seps2[1])
	if err != nil {
		return -1
	}
	return i1*60 + i2
}

func mergeFilesTo(files []AudFile, name string) error {
	wr, err := createFileLikeExam(name, files[0].Name)
	if err != nil {
		return err
	}
	defer wr.Close()
	total := 0
	zero := []byte{0x0}
	for _, f := range files {
		rd, err := openWav(f.Name)
		if err != nil {
			return err
		}
		bps := rd.GetBytesPerSec()
		bufWr := bufio.NewWriter(wr)
		if f.Delay*int(bps)-total > 0 {
			for i := 0; i < (f.Delay*int(bps) - total); i++ {
				bufWr.Write(zero)
			}
			err = bufWr.Flush()
			if err != nil {
				return err
			}
			total = f.Delay * int(bps)
		}
		wavByteLength := 0
		for {
			buf, err := rd.ReadRawSample()
			if err != nil {
				break
			}
			n, err := bufWr.Write(buf)
			if err != nil {
				return err
			}
			if n != len(buf) {
				fmt.Println("write error:", len(buf), n)
			}
			wavByteLength += n
		}
		err = bufWr.Flush()
		if err != nil {
			return err
		}
		total += wavByteLength
		seconds := wavByteLength / int(bps)
		fmt.Println(f.Name, seconds)
	}
	return nil
}

func createFileLikeExam(name, exam string) (ret *wav.Writer, err error) {
	info, err := os.Stat(exam)
	if err != nil {
		return nil, err
	}
	fp, err := os.Open(exam)
	if err != nil {
		return nil, err
	}
	defer fp.Close()
	r1, err := wav.NewReader(fp, info.Size())
	if err != nil {
		return nil, err
	}
	fmt := r1.GetFile()
	out, err := os.Create(name)
	if err != nil {
		return nil, err
	}
	ret, err = fmt.NewWriter(out)
	return
}

func openWav(name string) (ret *wav.Reader, err error) {
	info, err := os.Stat(name)
	if err != nil {
		return nil, err
	}
	fp, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	ret, err = wav.NewReader(fp, info.Size())
	return
}
